Skip to content

Add USD-denominated price chart#787

Merged
realproject7 merged 4 commits intomainfrom
task/776-chart-usd-research
Apr 3, 2026
Merged

Add USD-denominated price chart#787
realproject7 merged 4 commits intomainfrom
task/776-chart-usd-research

Conversation

@realproject7
Copy link
Copy Markdown
Owner

Summary

  • Adds reserve_usd_rate and rate_source columns to trade_history table (migration 00031)
  • Both trade indexers (cron + webhook) now fetch and store PLOT/USD rate at index time via lib/reserve-usd-rate.ts
  • PriceChart.tsx gains a USD/PLOT segmented toggle (defaults to USD when data available)
  • Approximate data points rendered with dashed lines + footnote disclosure (per T2a review)
  • Backfill script (scripts/backfill-usd-rates.ts) with tiered strategy: exact historical reads via archive-capable RPCs, falling back to approximate current-rate extrapolation

Design Decisions

  • PLOT/USD derivation: priceForNextMint(PLOT_TOKEN) in HUNT × HUNT/USD via 1inch spot oracle
  • Backfill accuracy: Exact where archive RPC supports historical eth_call; approximate otherwise. Rows tagged with rate_source (live, backfill_exact, backfill_approx) for future re-backfill
  • NULL handling: If USD rate fetch fails, reserve_usd_rate = NULL, chart falls back to reserve-only display
  • No archive node dependency: Works with public RPCs; some (mainnet.base.org, base.drpc.org) happen to support historical reads

Approximate Data Disclosure

Per T2a review requirement: approximate USD data points are visually distinguished with dashed line segments and reduced opacity, plus a footnote when the chart contains any approximate data.

Test Plan

  • Run migration against Supabase
  • Verify cron indexer stores reserve_usd_rate and rate_source='live' on new trades
  • Verify webhook indexer stores USD rate on real-time trades
  • Test PriceChart toggle between USD and PLOT modes
  • Test chart with NULL reserve_usd_rate rows (should fall back to reserve-only)
  • Run npx tsx scripts/backfill-usd-rates.ts to backfill existing trades
  • Verify dashed line rendering for backfill_approx data points

Fixes #345

🤖 Generated with Claude Code

Add reserve_usd_rate column to trade_history to store PLOT/USD at trade
time. Both cron and webhook indexers now fetch and persist the rate.
PriceChart gains a USD/PLOT toggle with dashed lines for approximate
historical data. Includes tiered backfill script (exact via archive RPC,
approximate fallback).

Fixes #345

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 3, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
plotlink Ignored Ignored Apr 3, 2026 7:30am

Request Review

- Extract ONEINCH_SPOT_PRICE_AGGREGATOR to lib/contracts/constants.ts
- Extract spotPriceAbi to lib/contracts/abi.ts
- Add CHECK constraint on rate_source column
- Document USDC 6-decimal assumption

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Collaborator

@project7-interns project7-interns left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: REQUEST CHANGES

Summary

The overall direction is sound, but the current implementation still writes materially incorrect USD history in a couple of paths. I would not ship this as-is because it can present stale or miscomputed USD prices as if they were historical truth.

Findings

  • [high] The approximate backfill path does not implement the strategy described in the PR. getCurrentPlotUsd() fetches a single current PLOT/USD rate and reuses that same value for every historical block, so rows marked backfill_approx ignore historical HUNT/USD entirely.

    • File: scripts/backfill-usd-rates.ts:91
    • Suggestion: fetch historical HUNT/USD for each trade date/block in the approximate path, or explicitly rename/reframe this source as a current-rate extrapolation instead of a historical approximation.
  • [medium] The cron indexer fetches one current PLOT/USD rate per 200-block scan and stamps it onto every trade in that batch as live. If the cursor is catching up after downtime, those rows can be several minutes or more away from the actual trade time while still being labeled as exact live data.

    • File: src/app/api/cron/trade-history/route.ts:90
    • Suggestion: fetch/cache the reserve USD rate per unique block (or per event timestamp bucket), or mark batch-stamped rows differently so they are not indistinguishable from exact-at-trade-time data.
  • [medium] In USD mode the chart filters out rows with reserve_usd_rate = null, then uses the last remaining USD point as latest. If the newest trades are missing a USD rate, the component will silently hide those rows and label an older point as the latest price.

    • File: src/components/PriceChart.tsx:132
    • Suggestion: do not collapse missing-rate rows out of the timeline when presenting a latest USD price. Either show a visible gap/stale state, or only default to USD when the newest displayed trade has a USD rate.

Decision

Requesting changes because the current data pipeline can write or display inaccurate USD history without clearly communicating that loss of precision.

…test

- Backfill script now reads historical HUNT/USD per-block instead of
  using a single current PLOT/USD for all rows
- Cron indexer marks trades as 'backfill_approx' during catch-up scans
  (>200 blocks behind head) instead of falsely labeling as 'live'
- PriceChart labels last USD point as "last USD" (not "latest") when
  newer trades exist without USD data

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Collaborator

@project7-interns project7-interns left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: REQUEST CHANGES

Summary

Two of my prior concerns look addressed, but one correctness issue remains in the cron path. The current catch-up detection still allows materially stale USD rates to be labeled as live for part of the final backlog batch.

Findings

  • [medium] isCatchUp is derived from currentBlock - toBlock, but the batch writes one shared reserve_usd_rate / rate_source across the whole [fromBlock, toBlock] window. In the final catch-up batch, toBlock can be within 200 blocks of head while fromBlock is still nearly 400 blocks behind, so older trades in that same batch are stamped as live even though they are not near-head.
    • File: src/app/api/cron/trade-history/route.ts:94
    • Suggestion: base the label on the oldest block in the batch (for example currentBlock - fromBlock), or compute/cache the reserve USD rate per event block / smaller bucket so that only truly near-head trades get live.

Decision

Keeping this in request-changes until the cron labeling semantics match the precision of the data being written.

… check

Use fromBlock (oldest block in batch) instead of toBlock to determine
catch-up status. Prevents the final catch-up batch from mislabeling
older trades as 'live' when toBlock happens to be near head.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Collaborator

@project7-interns project7-interns left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: APPROVE

Summary

The remaining cron-labeling issue is fixed. Using fromBlock for the catch-up check is the conservative behavior I was asking for, and I do not have further correctness blockers on this PR.

Findings

  • None.

Decision

Approved based on the current implementation and the issues addressed in the follow-up revisions.

@realproject7 realproject7 merged commit 55b13a9 into main Apr 3, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants